{*******************************************************************************
* The contents of this file are used with permission, subject to the Mozilla   *
* Public License Version 1.1 (the "License"); you may not use this file except *
* in compliance with the License. You may obtain a copy of the License at      *
* http://www.mozilla.org/MPL/                                                  *
*                                                                              *
* Software distributed under the License is distributed on an "AS IS" basis,   *
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for *
* the specific language governing rights and limitations under the License.    *
*                                                                              *
* The Original Code is distributed as part of the "mediate" product and is     *
* Copyright (C) @home media limited. All Rights Reserved.                      *
*                                                                              *
* Based on original code from DSPack 'RGB24 Video Renderer' demo               *
* Copyright (C) 2004 Milenko Mitrovic <dcoder@dsp-worx.de>                     *
*                                                                              *
* Email: support@athomemedia.co.uk                                             *
* Web:   http://www.athomemedia.co.uk                                          *
*******************************************************************************}

unit DirectShowRenderer;

interface

uses
  BaseClass, Direct3D9, DirectShow9, Windows;

const
  CLSID_MediateVideoRenderer: TGUID = '{0A08B009-AAFE-425E-8036-56214CEFFDE3}';
  IID_IMediateVideoConfig: TGUID = '{5E6EC01C-D932-49B0-8172-A9B738AE82D3}';

type
  PMediateRendererSetup = ^TMediateRendererSetup;
  TMediateRendererSetup = record
    Width: DWORD;
    Height: DWORD;
    Format: TD3DFormat;
  end;

  // This interface is implemented by our DirectX rendering code
  IMediateVideoRenderer = interface
    ['{A67902C4-041D-4228-8FDE-B50E950BB923}']
    function InitialiseRenderer(Info: PMediateRendererSetup): HResult; stdcall;
    function TerminateRenderer: HResult; stdcall;
    function PresentMediateFrame(Frame: IMediaSample): HResult; stdcall;
  end;

  // This interface is implemented here to allow connection to the renderer
  IMediateVideoConfig = interface
    ['{5E6EC01C-D932-49B0-8172-A9B738AE82D3}']
    function ConnectRenderer(Client: IMediateVideoRenderer): HResult; stdcall;
  end;


  TAHMDXVideoRenderer = class(TBCBaseVideoRenderer, IMediateVideoConfig,
                              IBasicVideo2, IAMFilterMiscFlags)
  private
    FFormat: TVideoInfoHeader;
    FClient: IMediateVideoRenderer;
  public
    constructor Create(ObjName: String; Unk: IUnknown; out hr : HResult);
    constructor CreateFromFactory(Factory: TBCClassFactory; const Controller: IUnknown); override;
    function CheckMediaType(MediaType: PAMMediaType): HResult; override;
    function DoRenderSample(MediaSample: IMediaSample): HResult; override;
    procedure OnReceiveFirstSample(MediaSample: IMediaSample); override;
    function SetMediaType(MediaType: PAMMediaType): HResult; override;
    function Active: HResult; override;
    function Inactive: HResult; override;
    // IDispatch interface methods
    function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
    function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
    function GetIDsOfNames(const IID: TGUID; Names: Pointer; NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
    function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
    // IMediateVideoConfig interface methods
    function ConnectRenderer(Client: IMediateVideoRenderer): HResult; stdcall;
    // IBasicVideo interface methods
    function get_AvgTimePerFrame(out pAvgTimePerFrame: TRefTime): HResult; stdcall;
    function get_BitRate(out pBitRate: Longint): HResult; stdcall;
    function get_BitErrorRate(out pBitErrorRate: Longint): HResult; stdcall;
    function get_VideoWidth(out pVideoWidth: Longint): HResult; stdcall;
    function get_VideoHeight(out pVideoHeight: Longint): HResult; stdcall;
    function put_SourceLeft(SourceLeft: Longint): HResult; stdcall;
    function get_SourceLeft(out pSourceLeft: Longint): HResult; stdcall;
    function put_SourceWidth(SourceWidth: Longint): HResult; stdcall;
    function get_SourceWidth(out pSourceWidth: Longint): HResult; stdcall;
    function put_SourceTop(SourceTop: Longint): HResult; stdcall;
    function get_SourceTop(out pSourceTop: Longint): HResult; stdcall;
    function put_SourceHeight(SourceHeight: Longint): HResult; stdcall;
    function get_SourceHeight(out pSourceHeight: Longint): HResult; stdcall;
    function put_DestinationLeft(DestinationLeft: Longint): HResult; stdcall;
    function get_DestinationLeft(out pDestinationLeft: Longint): HResult; stdcall;
    function put_DestinationWidth(DestinationWidth: Longint): HResult; stdcall;
    function get_DestinationWidth(out pDestinationWidth: Longint): HResult; stdcall;
    function put_DestinationTop(DestinationTop: Longint): HResult; stdcall;
    function get_DestinationTop(out pDestinationTop: Longint): HResult; stdcall;
    function put_DestinationHeight(DestinationHeight: Longint): HResult; stdcall;
    function get_DestinationHeight(out pDestinationHeight: Longint): HResult; stdcall;
    function SetSourcePosition(Left, Top, Width, Height: Longint): HResult; stdcall;
    function GetSourcePosition(out pLeft, pTop, pWidth, pHeight: Longint): HResult; stdcall;
    function SetDefaultSourcePosition: HResult; stdcall;
    function SetDestinationPosition(Left, Top, Width, Height: Longint): HResult; stdcall;
    function GetDestinationPosition(out pLeft, pTop, pWidth, pHeight: Longint): HResult; stdcall;
    function SetDefaultDestinationPosition: HResult; stdcall;
    function GetVideoSize(out pWidth, Height: Longint): HResult; stdcall;
    function GetVideoPaletteEntries(StartIndex, Entries: Longint; out pRetrieved: Longint; out pPalette): HResult; stdcall;
    function GetCurrentImage(var BufferSize: Longint; var pDIBImage): HResult; stdcall;
    function IsUsingDefaultSource: HResult; stdcall;
    function IsUsingDefaultDestination: HResult; stdcall;
    // IBasicVideo2 interface methods
    function GetPreferredAspectRatio(out plAspectX, plAspectY: Longint): HResult; stdcall;
    // IAMFilterMiscFlags interface methods
    function GetMiscFlags: ULONG; stdcall;
  end;

implementation

uses SysUtils;

function CheckConnected(Pin : TBCBasePin; out Res : HRESULT) : Boolean;
begin
  if not Pin.IsConnected then
  begin
    Res := VFW_E_NOT_CONNECTED;
    Result := False;
  end else
  begin
    Res := S_OK;
    Result := True;
  end;
end;

constructor TAHMDXVideoRenderer.Create(ObjName: String; Unk: IUnknown; out hr: HResult);
begin
  inherited Create(CLSID_MediateVideoRenderer, PAnsiChar(ObjName), Unk, hr);
end;

constructor TAHMDXVideoRenderer.CreateFromFactory(Factory: TBCClassFactory; const Controller: IUnknown);
var
  hr: HRESULT;
begin
  Create(Factory.Name, Controller, hr);
end;

function TAHMDXVideoRenderer.Active: HResult;
begin
  Result := inherited Active;
end;

function TAHMDXVideoRenderer.Inactive: HResult;
begin
  Result := inherited Inactive;
end;

function TAHMDXVideoRenderer.CheckMediaType(MediaType: PAMMediaType): HResult;
begin
  if (MediaType = nil) then
  begin
    Result := E_POINTER;
    Exit;
  end;

  // Specify 32 bit video format to match DirectX texture format
  if not IsEqualGUID(MediaType.majortype, MEDIATYPE_Video) or
     not IsEqualGUID(MediaType.subtype, MEDIASUBTYPE_RGB32) or
//     not IsEqualGUID(MediaType.subtype, MEDIASUBTYPE_RGB32_D3D_DX9_RT) or
     not IsEqualGUID(MediaType.formattype, FORMAT_VideoInfo) then
  begin
    Result := E_INVALIDARG;
    Exit;
  end;

  Result := NOERROR;
end;

function TAHMDXVideoRenderer.DoRenderSample(MediaSample: IMediaSample): HResult;
begin
  if MediaSample = nil then Result := E_POINTER
  else if FClient = nil then Result := E_UNEXPECTED
  else Result := FClient.PresentMediateFrame(MediaSample);
end;

procedure TAHMDXVideoRenderer.OnReceiveFirstSample(MediaSample: IMediaSample);
begin
  DoRenderSample(MediaSample);
end;

function TAHMDXVideoRenderer.SetMediaType(MediaType: PAMMediaType): HResult;
var
  VIH: PVIDEOINFOHEADER;
  Setup: TMediateRendererSetup;
begin
  if (MediaType = nil) or (FClient = nil) then
  begin
    Result := E_POINTER;
    Exit;
  end;

  VIH := PVIDEOINFOHEADER(MediaType.pbFormat);
  if (VIH = nil) then
  begin
    Result := E_UNEXPECTED;
    Exit;
  end;

  CopyMemory(@FFormat,VIH,SizeOf(TVideoInfoHeader));

  Setup.Width := FFormat.bmiHeader.biWidth;
  Setup.Height := FFormat.bmiHeader.biHeight;
  Setup.Format := D3DFMT_R8G8B8;

  // Try various texture formats for client compatibility
  Result := FClient.InitialiseRenderer(@Setup);
  if Result = E_FAIL then
  begin
    FClient.TerminateRenderer;
    Setup.Format := D3DFMT_A8R8G8B8;
    Result := FClient.InitialiseRenderer(@Setup);
  end;
end;

// IDispatch interface methods

function TAHMDXVideoRenderer.GetTypeInfoCount(out Count: Integer): HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.GetIDsOfNames(const IID: TGUID; Names: Pointer; NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

// IMediateVideoConfig interface methods

function TAHMDXVideoRenderer.ConnectRenderer(Client: IMediateVideoRenderer): HResult;
begin
  // Store reference to renderer client
  if Client <> nil then
  begin
    FClient := Client;
    Result := S_OK;
  end
  else
    Result := E_POINTER;
end;

// IBasicVideo interface methods

function TAHMDXVideoRenderer.get_AvgTimePerFrame(out pAvgTimePerFrame: TRefTime): HResult; stdcall;
begin
  if not CheckConnected(FInputPin,Result) then Exit;
  pAvgTimePerFrame := FFormat.AvgTimePerFrame;
  Result := NOERROR;
end;

function TAHMDXVideoRenderer.get_BitRate(out pBitRate: Longint): HResult; stdcall;
begin
  if not CheckConnected(FInputPin,Result) then Exit;
  pBitRate := FFormat.dwBitRate;
  Result := NOERROR;
end;

function TAHMDXVideoRenderer.get_BitErrorRate(out pBitErrorRate: Longint): HResult; stdcall;
begin
  if not CheckConnected(FInputPin,Result) then Exit;
  pBitErrorRate := FFormat.dwBitErrorRate;
  Result := NOERROR;
end;

function TAHMDXVideoRenderer.get_VideoWidth(out pVideoWidth: Longint): HResult; stdcall;
begin
  if not CheckConnected(FInputPin,Result) then Exit;
  pVideoWidth := FFormat.bmiHeader.biWidth;
  Result := NOERROR;
end;

function TAHMDXVideoRenderer.get_VideoHeight(out pVideoHeight: Longint): HResult; stdcall;
begin
  if not CheckConnected(FInputPin,Result) then Exit;
  pVideoHeight := FFormat.bmiHeader.biHeight;
  Result := NOERROR;
end;

function TAHMDXVideoRenderer.put_SourceLeft(SourceLeft: Longint): HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.get_SourceLeft(out pSourceLeft: Longint): HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.put_SourceWidth(SourceWidth: Longint): HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.get_SourceWidth(out pSourceWidth: Longint): HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.put_SourceTop(SourceTop: Longint): HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.get_SourceTop(out pSourceTop: Longint): HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.put_SourceHeight(SourceHeight: Longint): HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.get_SourceHeight(out pSourceHeight: Longint): HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.put_DestinationLeft(DestinationLeft: Longint): HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.get_DestinationLeft(out pDestinationLeft: Longint): HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.put_DestinationWidth(DestinationWidth: Longint): HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.get_DestinationWidth(out pDestinationWidth: Longint): HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.put_DestinationTop(DestinationTop: Longint): HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.get_DestinationTop(out pDestinationTop: Longint): HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.put_DestinationHeight(DestinationHeight: Longint): HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.get_DestinationHeight(out pDestinationHeight: Longint): HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.SetSourcePosition(Left, Top, Width, Height: Longint): HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.GetSourcePosition(out pLeft, pTop, pWidth, pHeight: Longint): HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.SetDefaultSourcePosition: HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.SetDestinationPosition(Left, Top, Width, Height: Longint): HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.GetDestinationPosition(out pLeft, pTop, pWidth, pHeight: Longint): HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.SetDefaultDestinationPosition: HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.GetVideoSize(out pWidth, Height: Longint): HResult; stdcall;
begin
  if not CheckConnected(FInputPin,Result) then Exit;
  pWidth := FFormat.bmiHeader.biWidth;
  Height := FFormat.bmiHeader.biHeight;
  Result := NOERROR;
end;

function TAHMDXVideoRenderer.GetVideoPaletteEntries(StartIndex, Entries: Longint; out pRetrieved: Longint; out pPalette): HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.GetCurrentImage(var BufferSize: Longint; var pDIBImage): HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.IsUsingDefaultSource: HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAHMDXVideoRenderer.IsUsingDefaultDestination: HResult; stdcall;
begin
  Result := E_NOTIMPL;
end;

// IBasicVideo2 interface methods

function TAHMDXVideoRenderer.GetPreferredAspectRatio(out plAspectX, plAspectY: Longint): HResult; stdcall;
begin
  Result := GetVideoSize(plAspectX, plAspectY);
end;

// IAMFilterMiscFlags interface methods

function TAHMDXVideoRenderer.GetMiscFlags: ULONG; stdcall;
begin
  Result := AM_FILTER_MISC_FLAGS_IS_RENDERER;
end;


end.

